#include <GL/glew.h>
#include <GL/freeglut.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <cstdio>
#include <ctime>
#include <common/common.hpp>
#include "particle_shader.hpp"
#include "particle_system.hpp"
#include "particle_shooter.hpp"

#define rgb(r, g, b) ((r << 16) + (g << 8) + b)

const vector FIXED_DIR(0, 0.5f, 0);
const float ANGLE_VARIANCE_DEGREE = 5.0f;
const float SPEED_VARIANCE = 1.0f;

particle_shader _shader;
particle_system _particle(10000);
particle_shooter _redshooter(point(-1.0f, 0, 0), FIXED_DIR, rgb(255, 50, 5), ANGLE_VARIANCE_DEGREE, SPEED_VARIANCE);
particle_shooter _greenshooter(point(0, 0, 0), FIXED_DIR, rgb(25, 255, 25), ANGLE_VARIANCE_DEGREE, SPEED_VARIANCE);
particle_shooter _blueshooter(point(1.0f, 0, 0), FIXED_DIR, rgb(5, 50, 255), ANGLE_VARIANCE_DEGREE, SPEED_VARIANCE);

GLuint _texid;
float _runsecond = 0.0f; // the second from start
glm::mat4 _view_projection_mat;

static GLboolean
init() {
	GLenum err = glewInit();
	if (err != GLEW_OK) {
		logv("fail to init glew");
		return GL_FALSE;
	}

	if (!GLEW_VERSION_3_3) {
		logv("not support for ogl3.3");
		return GL_FALSE;
	}
	logv("support for ogl3.3");

	_texid = texture_util::loadtexture("./res/particle_texture.png");
	if (_texid == 0) {
		logv("fail to load texture file");
		return GL_FALSE;
	}

	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);

	glEnable(GL_POINT_SPRITE); // to enable access gl_PointCoord in fragment shader
	glEnable(GL_VERTEX_PROGRAM_POINT_SIZE); // to enable set gl_PointSize in vertex shader
	glEnable(GL_BLEND);
	glBlendFunc(GL_ONE, GL_ONE);

    if (!_shader.init("./res/particle_vertex_shader.glsl", "./res/particle_fragment_shader.glsl")) {
    	logv("fail to init particle shader");
    	return GL_FALSE;
    }

    if (!_particle.init()) {
    	logv("fail to init particle system");
    	return GL_FALSE;
    }

    srand((unsigned int)time(NULL));

    logv("init finish successfully");
    return GL_TRUE;
}

static void
cb_display() {
	// logv("cb_display");
   	glClear(GL_COLOR_BUFFER_BIT);

   	_redshooter.addparticles(_particle, _runsecond, 5);
   	_greenshooter.addparticles(_particle, _runsecond, 5);
   	_blueshooter.addparticles(_particle, _runsecond, 5);

   	_shader.useprogram();
   	_shader.setuniform(_view_projection_mat, _runsecond, _texid);
   	_particle.binddata();
   	_particle.draw();
	
	glutSwapBuffers(); // or we can simply call glFlush
}

static void 
cb_reshape(int width, int height) {
	logv("cb_reshape: width:%d, height:%d", width, height);
	glViewport (0, 0, width, height);

	GLfloat aspect = (GLfloat)width / (GLfloat)height;
	glm::mat4x4 persmat = glm::perspective(glm::radians(45.0f), aspect, 1.0f, 10.0f);
	glm::mat4x4 viewmat = glm::translate(glm::mat4(), glm::vec3(0, -1.5f, -5.0f));
	_view_projection_mat = persmat * viewmat;
}

static void
cb_close() {
	logv("cb_close");
	_shader.deleteprogram();
	glDeleteTextures(1, &_texid);
}

static void
cb_timer(int param) {
	_runsecond += 0.017f;
	glutPostRedisplay();
	glutTimerFunc(17, cb_timer, 0);
}

int
main(int argc, char *argv[]) {
	glutInit(&argc, argv);
	glutInitWindowSize(800, 600);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
	glutInitWindowPosition(0, 0);
	
	glutCreateWindow("AirHockey - particles");
	
	if (init() == GL_FALSE)
		return 0;

	glutDisplayFunc(cb_display);
	glutReshapeFunc(cb_reshape);
	glutCloseFunc(cb_close);
	glutTimerFunc(17, cb_timer, 0); // 60 fps

	glutMainLoop();
	return 0;
}
